/**
 * HTTP请求工具类
 * 提供统一的HTTP请求封装，包括认证、拦截器、错误处理等功能
 * 
 * @author Cascade
 * @version 2.0.0
 */

import {  userInfo, clearUser, saveUserInfo } from '@/stores/user'
import type { LoginResult, Data } from '@/types'
import { baseURL, FORM_CONTENT_TYPE, generateBasicAuth } from './config'




/**
 *  封装请求方法
 * @template T 响应数据中 `data` 字段的类型，用于指定接口返回的核心数据类型
 * @param options 请求参数
 */
export const http = <T>(options : any) => {
	return new Promise<Data<T>>((resolve, reject) => {
		uni.request({
			// 1.请求参数
			...options,
			// 2.响应成功
			success(res: any) {
				handleResponse<T>(res, options, resolve, reject);
			},
			// 3.响应失败
			fail(err: any) {
				uni.showToast({
					icon: 'none',
					title: '网络错误，换个网络试试',
				})
				reject(err)
			},
		})
	})
}






// ==================== 响应处理函数 ====================

/**
 * 处理HTTP响应
 * @template T 响应数据类型
 * @param res 响应对象
 * @param options 请求选项
 * @param resolve Promise resolve函数
 * @param reject Promise reject函数
 */
const handleResponse = <T>(
	res: any,
	options: any,
	resolve: (value: Data<T>) => void,
	reject: (reason?: any) => void
): void => {
	console.log('res.statusCode ', res.statusCode )
	//2.1 状态码，这个是http状态码
	if (res.statusCode >= 200 && res.statusCode < 300) {
		if (isOAuth2LoginRequest(options.url)) {
			// OAuth2登录请求特殊处理
			const responseData = res.data;
			
			// 检查是否有业务错误码（登录失败的情况）
			if (responseData.code && responseData.code !== '0') {
				// 登录失败：显示错误信息并reject
				const errorMsg = responseData.msg || '登录失败';
				uni.showToast({
					icon: 'none',
					title: errorMsg,
					duration: 2000
				});
				reject(responseData);
			} else {
				// 登录成功：返回token信息
				resolve({
					code: 0,
					msg: '登录成功',
					data: responseData as LoginResult,
				} as Data<T>)
			}
		} else {
			// 检查业务状态码
			const responseData = res.data as Data<T>;
			
			// 业务成功：code为'0'
			if (responseData.code === 0) {
				resolve(responseData);
			} else {
				// 业务失败：code不为0，显示错误信息并reject
				const errorMsg = responseData.msg || '操作失败';
				uni.showToast({
					icon: 'none',
					title: errorMsg,
					duration: 2000
				});
				reject(responseData);
			}
		}
	} else if (res.statusCode === 401 || res.statusCode === 403 || res.statusCode === 424) {
		//2.2 都是未授权，那么就清理后台token
		console.warn('认证失效:', res.statusCode, res.data)
		handleTokenExpired()
		reject(res)
	} else { //2.3 其他错误
		uni.showToast({
			icon: 'none',
			title: (res.data as Data<T>).msg || '请求错误',
		})
		reject(res)
	}
};

// ==================== 工具函数 ====================

// 防止重复刷新token的标志
let isRefreshing = false;
let failedQueue: Array<{ resolve: Function; reject: Function }> = [];

/**
 * 处理令牌失效 - 尝试刷新token
 */
const handleTokenExpired = async () => {
	// 如果已经在刷新中，将请求加入队列
	if (isRefreshing) {
		return new Promise((resolve, reject) => {
			failedQueue.push({ resolve, reject });
		});
	}
	
	// 检查是否有refresh_token
	if (!userInfo.value?.refresh_token) {
		// 没有refresh_token，直接清理并跳转登录
		await clearUserAndRedirect();
		return;
	}
	
	isRefreshing = true;
	
	try {
		// 尝试刷新token
		const refreshResult = await refreshToken(userInfo.value.refresh_token);
		
		if (refreshResult) {
			// 刷新成功，更新用户信息
			const updatedUserInfo = {
				...userInfo.value,
				access_token: refreshResult.access_token,
				refresh_token: refreshResult.refresh_token,
				basicAuth: `Bearer ${refreshResult.access_token}`
			};
			
			userInfo.value = updatedUserInfo;
			saveUserInfo();
			
			// 处理队列中的请求
			processQueue(null);
			
			console.log('Token刷新成功');
		} else {
			throw new Error('刷新token失败');
		}
	} catch (error) {
		console.warn('Token刷新失败:', error);
		// 刷新失败，清理用户信息并跳转登录
		processQueue(error);
		await clearUserAndRedirect();
	} finally {
		isRefreshing = false;
	}
};

/**
 * 清理用户信息并跳转登录页面
 */
const clearUserAndRedirect = async () => {
	// 清理用户信息和令牌（跳过logout API调用，避免死循环）
	await clearUser(true);
	
	// 显示提示信息
	uni.showToast({
		icon: 'none',
		title: '登录已失效，请重新登录',
		duration: 2000
	});
	
	// 延迟跳转到登录页面
	setTimeout(() => {
		uni.reLaunch({
			url: '/pages/login/index'
		});
	}, 2000);
};

/**
 * 处理队列中的请求
 */
const processQueue = (error: any) => {
	failedQueue.forEach(({ resolve, reject }) => {
		if (error) {
			reject(error);
		} else {
			resolve(null);
		}
	});
	
	failedQueue = [];
};

/**
 * 刷新token
 */
const refreshToken = async (refreshTokenValue: string): Promise<LoginResult | null> => {
	try {
		// 导入getAuthApiPrefix函数来获取正确的认证API前缀
		const { getAuthApiPrefix } = await import('./config');
		
		const response = await uni.request({
			url: `${baseURL}${getAuthApiPrefix()}/oauth2/token`,
			method: 'POST',
			header: {
				'Authorization': generateBasicAuth(),
				'Content-Type': FORM_CONTENT_TYPE
			},
			data: {
				grant_type: 'refresh_token',
				refresh_token: refreshTokenValue
			}
		});
		
		if (response.statusCode === 200 && response.data) {
			return response.data as LoginResult;
		}
		
		return null;
	} catch (error) {
		console.error('刷新token请求失败:', error);
		return null;
	}
};

/**
 * 判断是否为OAuth2登录请求和token相关请求
 * @param url 请求URL
 * @returns 是否为OAuth2登录请求
 */
const isOAuth2LoginRequest = (url: string): boolean => {
	return url.includes('/oauth2/token') || url.includes('/token/check_token');
};

/**
 * 判断是否为无需认证的请求（登录、注册等）
 * @param url 请求URL
 * @returns 是否为无需认证的请求
 */
const isPublicRequest = (url: string): boolean => {
	const publicPaths = [
		'/oauth2/token',      // OAuth2登录接口
		'/register/',         // 注册接口
		'/code/image',        // 验证码接口
		'/token/check_token', // 检验token接口
	];
	return publicPaths.some(path => url.includes(path));
};



// ==================== 请求拦截器 ====================

/**
 * HTTP请求拦截器配置
 */
const httpInterceptor = {
	/**
	 * 请求前拦截处理
	 * @param options 请求选项
	 */
	invoke(options: any) {
		// 保存原始URL用于判断
		const originalUrl = options.url;
		
		// 1. URL处理：非HTTP开头需要拼接地址
		if (options.url.indexOf('http') !== 0) {
			options.url = baseURL + options.url;
		}

		// 2. 初始化请求头
		options.header = options.header || {};

		// 3. 设置请求超时时间
		options.timeout = 10000;

		// 4. 设置认证头和内容类型
		if (isOAuth2LoginRequest(originalUrl)) {
			// OAuth2登录请求：使用Basic认证
			options.header.Authorization =  generateBasicAuth();
		} else if (!isPublicRequest(originalUrl)) {
			// 非公开请求：使用Bearer认证（如果已登录）
			if (userInfo.value && userInfo.value.basicAuth) {
				options.header.Authorization = userInfo.value.basicAuth;
			}
		}
		// 公开请求（如注册）不添加认证头

		// 5. 设置认证相关请求的内容类型
		if(isOAuth2LoginRequest(originalUrl)){
			options.header['Content-Type'] = FORM_CONTENT_TYPE;
		}
		
	}
};

// 拦截 request 请求
uni.addInterceptor('request', httpInterceptor)
// 拦截 uploadFile 文件上传
uni.addInterceptor('uploadFile', httpInterceptor)

